"""
数据验证器模块
提供各种数据验证功能
"""
import re
import os
from typing import List, Tuple, Optional, Dict, Any
from datetime import datetime

class PasswordValidator:
    """密码验证器"""
    
    def __init__(self, min_length: int = 8, max_length: int = 128,
                 require_uppercase: bool = True, require_lowercase: bool = True,
                 require_digits: bool = True, require_special: bool = True):
        """
        初始化密码验证器
        
        Args:
            min_length: 最小长度
            max_length: 最大长度
            require_uppercase: 需要大写字母
            require_lowercase: 需要小写字母
            require_digits: 需要数字
            require_special: 需要特殊字符
        """
        self.min_length = min_length
        self.max_length = max_length
        self.require_uppercase = require_uppercase
        self.require_lowercase = require_lowercase
        self.require_digits = require_digits
        self.require_special = require_special
        
        # 常见弱密码列表
        self.weak_passwords = {
            '123456', 'password', '12345678', 'qwerty', '123456789',
            '12345', '1234', '111111', '1234567', 'dragon',
            '123123', 'baseball', 'abc123', 'football', 'monkey',
            'letmein', '696969', 'shadow', 'master', '666666',
            'qwertyuiop', '123321', 'mustang', '1234567890',
            'michael', '654321', 'superman', '1qaz2wsx', '7777777',
            'fuckyou', '121212', '000000', 'qazwsx', '123qwe',
            'killer', 'trustno1', 'jordan', 'jennifer', 'zxcvbnm',
            'asdfgh', 'hunter', 'buster', 'soccer', 'harley',
            'batman', 'andrew', 'tigger', 'sunshine', 'iloveyou',
            'fuckme', '2000', 'charlie', 'robert', 'thomas',
            'hockey', 'ranger', 'daniel', 'starwars', 'klaster',
            '112233', 'george', 'asshole', 'computer', 'michelle',
            'jessica', 'pepper', '1111', 'zxcvbn', '555555',
            '11111111', '131313', 'freedom', '777777', 'pass',
            'fuck', 'maggie', '159753', 'aaaaaa', 'ginger',
            'princess', 'joshua', 'cheese', 'amanda', 'summer',
            'love', 'ashley', '6969', 'nicole', 'chelsea',
            'biteme', 'matthew', 'access', 'yankees', '987654321',
            'dallas', 'austin', 'thunder', 'taylor', 'matrix'
        }
    
    def validate(self, password: str) -> Tuple[bool, List[str]]:
        """
        验证密码
        
        Args:
            password: 密码
            
        Returns:
            tuple: (是否有效, 错误消息列表)
        """
        errors = []
        
        if not password:
            errors.append("密码不能为空")
            return False, errors
        
        # 检查长度
        if len(password) < self.min_length:
            errors.append(f"密码长度至少{self.min_length}个字符")
        
        if len(password) > self.max_length:
            errors.append(f"密码长度不能超过{self.max_length}个字符")
        
        # 检查字符类型
        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        has_special = any(not c.isalnum() for c in password)
        
        if self.require_uppercase and not has_upper:
            errors.append("密码必须包含大写字母")
        
        if self.require_lowercase and not has_lower:
            errors.append("密码必须包含小写字母")
        
        if self.require_digits and not has_digit:
            errors.append("密码必须包含数字")
        
        if self.require_special and not has_special:
            errors.append("密码必须包含特殊字符")
        
        # 检查弱密码
        if password.lower() in self.weak_passwords:
            errors.append("密码过于简单，请使用更复杂的密码")
        
        # 检查连续字符
        if self._has_sequential_chars(password):
            errors.append("密码不能包含连续的字符")
        
        # 检查重复字符
        if self._has_repeating_chars(password):
            errors.append("密码不能包含重复的字符序列")
        
        return len(errors) == 0, errors
    
    def _has_sequential_chars(self, password: str) -> bool:
        """检查是否包含连续字符"""
        for i in range(len(password) - 2):
            if (ord(password[i]) + 1 == ord(password[i+1]) and
                ord(password[i+1]) + 1 == ord(password[i+2])):
                return True
        return False
    
    def _has_repeating_chars(self, password: str) -> bool:
        """检查是否包含重复字符序列"""
        for i in range(len(password) - 3):
            if password[i] == password[i+1] == password[i+2]:
                return True
        return False

class UsernameValidator:
    """用户名验证器"""
    
    def __init__(self, min_length: int = 3, max_length: int = 20,
                 allow_underscore: bool = True, allow_hyphen: bool = True):
        """
        初始化用户名验证器
        
        Args:
            min_length: 最小长度
            max_length: 最大长度
            allow_underscore: 允许下划线
            allow_hyphen: 允许连字符
        """
        self.min_length = min_length
        self.max_length = max_length
        self.allow_underscore = allow_underscore
        self.allow_hyphen = allow_hyphen
        
        # 保留用户名列表
        self.reserved_names = {
            'admin', 'administrator', 'root', 'system', 'test',
            'moderator', 'owner', 'manager', 'support', 'help',
            'info', 'contact', 'service', 'api', 'bot', 'robot'
        }
    
    def validate(self, username: str) -> Tuple[bool, List[str]]:
        """
        验证用户名
        
        Args:
            username: 用户名
            
        Returns:
            tuple: (是否有效, 错误消息列表)
        """
        errors = []
        
        if not username:
            errors.append("用户名不能为空")
            return False, errors
        
        # 检查长度
        if len(username) < self.min_length:
            errors.append(f"用户名长度至少{self.min_length}个字符")
        
        if len(username) > self.max_length:
            errors.append(f"用户名长度不能超过{self.max_length}个字符")
        
        # 检查字符类型
        pattern = r'^[a-zA-Z0-9'
        if self.allow_underscore:
            pattern += '_'
        if self.allow_hyphen:
            pattern += '-'
        pattern += ']+$'
        
        if not re.match(pattern, username):
            allowed_chars = "字母、数字"
            if self.allow_underscore:
                allowed_chars += "、下划线"
            if self.allow_hyphen:
                allowed_chars += "、连字符"
            errors.append(f"用户名只能包含{allowed_chars}")
        
        # 检查是否以字母开头
        if not username[0].isalpha():
            errors.append("用户名必须以字母开头")
        
        # 检查保留用户名
        if username.lower() in self.reserved_names:
            errors.append("该用户名不可用")
        
        # 检查连续特殊字符
        if self._has_consecutive_special_chars(username):
            errors.append("用户名不能包含连续的特殊字符")
        
        return len(errors) == 0, errors
    
    def _has_consecutive_special_chars(self, username: str) -> bool:
        """检查是否包含连续的特殊字符"""
        special_chars = '-_'
        for i in range(len(username) - 1):
            if username[i] in special_chars and username[i+1] in special_chars:
                return True
        return False

class EmailValidator:
    """邮箱验证器"""
    
    def __init__(self, check_mx: bool = False, check_disposable: bool = True):
        """
        初始化邮箱验证器
        
        Args:
            check_mx: 是否检查MX记录
            check_disposable: 是否检查一次性邮箱
        """
        self.check_mx = check_mx
        self.check_disposable = check_disposable
        
        # 一次性邮箱域名列表
        self.disposable_domains = {
            'tempmail.com', '10minutemail.com', 'guerrillamail.com',
            'mailinator.com', 'yopmail.com', 'throwawaymail.com',
            'fakeinbox.com', 'temp-mail.org', 'getairmail.com',
            'tmpmail.org', 'discard.email', 'temp-mail.io'
        }
        
        # 邮箱格式正则
        self.email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    
    def validate(self, email: str) -> Tuple[bool, List[str]]:
        """
        验证邮箱地址
        
        Args:
            email: 邮箱地址
            
        Returns:
            tuple: (是否有效, 错误消息列表)
        """
        errors = []
        
        if not email:
            errors.append("邮箱地址不能为空")
            return False, errors
        
        # 检查格式
        if not re.match(self.email_pattern, email):
            errors.append("邮箱格式不正确")
            return False, errors
        
        # 检查一次性邮箱
        if self.check_disposable:
            domain = email.split('@')[1].lower()
            if domain in self.disposable_domains:
                errors.append("不支持一次性邮箱")
        
        # 检查MX记录（可选）
        if self.check_mx:
            if not self._check_mx_record(email):
                errors.append("邮箱域名不存在")
        
        return len(errors) == 0, errors
    
    def _check_mx_record(self, email: str) -> bool:
        """检查MX记录"""
        try:
            import dns.resolver
            domain = email.split('@')[1]
            mx_records = dns.resolver.resolve(domain, 'MX')
            return len(mx_records) > 0
        except Exception:
            # 如果检查失败，默认通过
            return True

class ContentValidator:
    """内容验证器"""
    
    def __init__(self, max_length: int = 10000, 
                 allowed_tags: List[str] = None,
                 blocked_words: List[str] = None):
        """
        初始化内容验证器
        
        Args:
            max_length: 最大长度
            allowed_tags: 允许的HTML标签
            blocked_words: 屏蔽词列表
        """
        self.max_length = max_length
        self.allowed_tags = allowed_tags or [
            'p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre',
            'blockquote', 'ul', 'ol', 'li', 'a', 'img'
        ]
        self.blocked_words = blocked_words or []
    
    def validate(self, content: str, content_type: str = 'text') -> Tuple[bool, List[str]]:
        """
        验证内容
        
        Args:
            content: 内容
            content_type: 内容类型（text, html, markdown）
            
        Returns:
            tuple: (是否有效, 错误消息列表)
        """
        errors = []
        
        if not content:
            errors.append("内容不能为空")
            return False, errors
        
        # 检查长度
        if len(content) > self.max_length:
            errors.append(f"内容长度不能超过{self.max_length}个字符")
        
        # 检查屏蔽词
        blocked_found = self._check_blocked_words(content)
        if blocked_found:
            errors.append("内容包含不允许的词汇")
        
        # 检查链接数量（防止垃圾信息）
        if content_type == 'html':
            link_count = content.count('<a ')
            if link_count > 10:
                errors.append("内容包含过多链接")
        
        # 检查图片数量
        if content_type == 'html':
            img_count = content.count('<img')
            if img_count > 5:
                errors.append("内容包含过多图片")
        
        return len(errors) == 0, errors
    
    def _check_blocked_words(self, content: str) -> List[str]:
        """检查屏蔽词"""
        found_words = []
        content_lower = content.lower()
        
        for word in self.blocked_words:
            if word.lower() in content_lower:
                found_words.append(word)
        
        return found_words

class FileValidator:
    """文件验证器"""
    
    def __init__(self, max_size: int = 16 * 1024 * 1024,  # 16MB
                 allowed_extensions: List[str] = None,
                 allowed_mime_types: List[str] = None):
        """
        初始化文件验证器
        
        Args:
            max_size: 最大文件大小（字节）
            allowed_extensions: 允许的文件扩展名
            allowed_mime_types: 允许的MIME类型
        """
        self.max_size = max_size
        self.allowed_extensions = allowed_extensions or [
            'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp',
            'pdf', 'doc', 'docx', 'txt', 'md',
            'zip', 'rar', '7z'
        ]
        self.allowed_mime_types = allowed_mime_types or [
            'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp',
            'application/pdf', 'application/msword', 
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'text/plain', 'text/markdown',
            'application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed'
        ]
        
        # 扩展名到MIME类型的映射
        self.extension_to_mime = {
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'png': 'image/png',
            'gif': 'image/gif',
            'bmp': 'image/bmp',
            'webp': 'image/webp',
            'pdf': 'application/pdf',
            'doc': 'application/msword',
            'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'txt': 'text/plain',
            'md': 'text/markdown',
            'zip': 'application/zip',
            'rar': 'application/x-rar-compressed',
            '7z': 'application/x-7z-compressed'
        }
    
    def validate(self, file_stream, filename: str, 
                 check_mime_type: bool = True) -> Tuple[bool, List[str]]:
        """
        验证文件
        
        Args:
            file_stream: 文件流
            filename: 文件名
            check_mime_type: 是否检查MIME类型
            
        Returns:
            tuple: (是否有效, 错误消息列表)
        """
        errors = []
        
        if not file_stream or not filename:
            errors.append("文件不能为空")
            return False, errors
        
        # 检查文件大小
        try:
            current_pos = file_stream.tell()
            file_stream.seek(0, 2)  # 移动到文件末尾
            file_size = file_stream.tell()
            file_stream.seek(current_pos)  # 恢复位置
            
            if file_size > self.max_size:
                errors.append(f"文件大小不能超过{self.max_size // (1024*1024)}MB")
        except (AttributeError, OSError):
            errors.append("无法读取文件大小")
        
        # 检查文件扩展名
        file_extension = os.path.splitext(filename)[1].lower().lstrip('.')
        if file_extension not in self.allowed_extensions:
            errors.append(f"不支持的文件类型: {file_extension}")
        
        # 检查MIME类型
        if check_mime_type and file_extension in self.extension_to_mime:
            expected_mime = self.extension_to_mime[file_extension]
            # 这里可以添加实际的MIME类型检测逻辑
        
        # 检查文件名安全性
        if not self._is_safe_filename(filename):
            errors.append("文件名包含不安全的字符")
        
        return len(errors) == 0, errors
    
    def _is_safe_filename(self, filename: str) -> bool:
        """检查文件名是否安全"""
        # 防止路径遍历攻击
        if '..' in filename or '/' in filename or '\\' in filename:
            return False
        
        # 检查特殊字符
        unsafe_chars = ['<', '>', ':', '"', '|', '?', '*']
        for char in unsafe_chars:
            if char in filename:
                return False
        
        return True
    
    def get_file_info(self, file_stream, filename: str) -> Dict[str, Any]:
        """
        获取文件信息
        
        Args:
            file_stream: 文件流
            filename: 文件名
            
        Returns:
            dict: 文件信息
        """
        file_extension = os.path.splitext(filename)[1].lower().lstrip('.')
        
        # 获取文件大小
        try:
            current_pos = file_stream.tell()
            file_stream.seek(0, 2)
            file_size = file_stream.tell()
            file_stream.seek(current_pos)
        except (AttributeError, OSError):
            file_size = 0
        
        return {
            'filename': filename,
            'extension': file_extension,
            'size': file_size,
            'mime_type': self.extension_to_mime.get(file_extension, 'application/octet-stream'),
            'is_valid': file_extension in self.allowed_extensions and file_size <= self.max_size
        }